home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / pine3.07 / pico / os_unix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-30  |  27.7 KB  |  1,331 lines

  1. /*
  2.  * Program:    Operating system dependent routines - Ultrix 4.1
  3.  *
  4.  * Author:    Michael Seibel
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: mikes@cac.washington.edu
  11.  *
  12.  * Date:    6 Jan 1992
  13.  * Last Edited:    11 May 1992
  14.  *
  15.  * Copyright 1991 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  * Notes:
  35.  *
  36.  * - SGI IRIX 4.0.1 port by:
  37.  *       johnb@edge.cis.mcmaster.ca,  2 April 1992
  38.  *
  39.  * - Dynix/PTX port by:
  40.  *       Donn Cave, UCS/UW, 15 April 1992
  41.  *
  42.  * - Probably have to break this up into separate os_type.c files since
  43.  *   the #ifdef's are getting a bit cumbersome.
  44.  *
  45.  */
  46.  
  47. #include     <stdio.h>
  48. #include    <errno.h>
  49. #include    <setjmp.h>
  50. #include    <time.h>
  51. #include    <pwd.h>
  52.  
  53. #include    "osdep.h"
  54. #include    "estruct.h"
  55. #include        "edef.h"
  56. #include        "pico.h"
  57. #include    <fcntl.h>
  58. #include    <sys/wait.h>
  59. #include    <sys/file.h>
  60.  
  61. #ifdef    POSIX
  62. #include    <termios.h>
  63.  
  64. struct termios nstate,
  65.         ostate;
  66. #else
  67. #if    defined(sv3) || defined(sgi)
  68. #include    <termio.h>
  69.  
  70. struct termio nstate,
  71.               ostate;
  72.  
  73. #else
  74. struct  sgttyb  ostate;                /* saved tty state */
  75. struct  sgttyb  nstate;                /* values for editor mode */
  76. struct  tchars    otchars;            /* old term special chars */
  77. struct  tchars    ntchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  78.                         /* A lot of nothing */
  79. #endif    /* sv3 || sgi */
  80. #endif    /* POSIX */
  81.  
  82. extern    int rtfrmshell();            /* return from suspn'd shell */
  83.  
  84. static  jmp_buf    alrm_state;            /* stack env for SIGALRM */
  85.  
  86. #ifdef    bsd
  87. int    errno;                    /* ya, I know... */
  88. #endif
  89.  
  90. #if    defined(dyn)
  91. #define    SIGTYPE int
  92. #else
  93. #define    SIGTYPE    void
  94. #endif
  95.  
  96. SIGTYPE    do_hup_signal();
  97. SIGTYPE    do_alarm_signal();
  98. #ifdef    TIOCGWINSZ
  99. SIGTYPE    winch_handler();
  100. #endif
  101.  
  102. /*
  103.  * for alt_editor arg[] building
  104.  */
  105. #define    MAXARGS    10
  106.  
  107. /*
  108.  * ttopen - this function is called once to set up the terminal device 
  109.  *          streams.  if called as pine composer, don't mess with
  110.  *          tty modes, but set signal handlers.
  111.  */
  112. ttopen()
  113. {
  114.     if(Pmaster == NULL){
  115. #ifdef    POSIX
  116.     tcgetattr (0, &ostate);
  117.     tcgetattr (0, &nstate);
  118.     nstate.c_lflag &= ~(ISIG | ICANON | ECHO);
  119.     nstate.c_iflag &= ~ICRNL;
  120.     nstate.c_oflag &= ~(ONLCR | OPOST);
  121.     nstate.c_cc[VMIN] = 1;
  122.     nstate.c_cc[VTIME] = 0;
  123.     tcsetattr (0, TCSADRAIN, &nstate);
  124. #else
  125. #if    defined(sv3) || defined(sgi)
  126.     (void) ioctl(0, TCGETA, &ostate);
  127.     (void) ioctl(0, TCGETA, &nstate);    /** again! **/
  128.  
  129.     nstate.c_lflag &= ~(ICANON | ISIG | ECHO);    /* noecho raw mode  */
  130.     nstate.c_oflag &= ~(OPOST | ONLCR);
  131.     nstate.c_iflag &= ~ICRNL;
  132.         
  133.     nstate.c_cc[VMIN] = '\01';  /* minimum # of chars to queue  */
  134.     nstate.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
  135.     (void) ioctl(0, TCSETA, &nstate);
  136. #else
  137.     ioctl(0, TIOCGETP, &ostate);        /* save old state */
  138.     ioctl(0, TIOCGETP, &nstate);        /* get base of new state */
  139.     nstate.sg_flags |= RAW;
  140.     nstate.sg_flags &= ~(ECHO|CRMOD);    /* no echo for now... */
  141.     ioctl(0, TIOCSETP, &nstate);        /* set mode */
  142.     
  143.     ioctl(0, TIOCGETC, &otchars);        /* Save old characters */
  144.     ioctl(0, TIOCSETC, &ntchars);        /* put new character into K */
  145. #endif    /* sv3 */
  146. #endif    /* POSIX */
  147.     }
  148.  
  149.     signal(SIGHUP, (SIGTYPE *)do_hup_signal);    /* deal with SIGHUP */
  150.     signal(SIGALRM, (SIGTYPE *)do_alarm_signal); /* timer for new mail */
  151. #ifdef    SIGTSTP
  152.     signal(SIGTSTP, SIG_DFL);
  153. #endif
  154. #ifdef    TIOCGWINSZ
  155.     signal(SIGWINCH, (SIGTYPE *)winch_handler); /* window size changes */
  156. #endif
  157. }
  158.  
  159.  
  160.  
  161. /*
  162.  * ttclose - this function gets called just before we go back home to 
  163.  *           the command interpreter.  If called as pine composer, don't
  164.  *           worry about modes, but set signals to default, pine will 
  165.  *           rewire things as needed.
  166.  */
  167. ttclose()
  168. {
  169.     if(Pmaster){
  170.     signal(SIGHUP, SIG_DFL);
  171.     signal(SIGALRM, SIG_DFL);
  172. #ifndef    sv3
  173.     signal(SIGCONT, SIG_DFL);
  174. #endif
  175. #ifdef    TIOCGWINSZ
  176.     signal(SIGWINCH, SIG_DFL);
  177. #endif
  178.     }
  179.     else{
  180.     if((gmode&MDFKEY) && Pmaster == NULL)
  181.       puts("\033[99l");        /* reset UW-NCSA telnet keys */
  182.     
  183. #ifdef    POSIX
  184.     tcsetattr (0, TCSADRAIN, &ostate);
  185. #else
  186. #if    defined(sv3) || defined(sgi)
  187.         ioctl(0, TCSETA, &ostate);
  188. #else
  189.     ioctl(0, TIOCSETP, &ostate);
  190.     ioctl(0, TIOCSETC, &otchars);
  191. #endif    /* sv3 || sgi */
  192. #endif    /* POSIX */
  193.     }
  194. }
  195.  
  196.  
  197. /*
  198.  * ttputc - Write a character to the display. 
  199.  */
  200. ttputc(c)
  201. {
  202.     putc(c, stdout);
  203. }
  204.  
  205.  
  206. /*
  207.  * ttflush - flush terminal buffer. Does real work where the terminal 
  208.  *           output is buffered up. A no-operation on systems where byte 
  209.  *           at a time terminal I/O is done.
  210.  */
  211. ttflush()
  212. {
  213.     fflush(stdout);
  214. }
  215.  
  216.  
  217. /*
  218.  * ttgetc - Read a character from the terminal, performing no editing 
  219.  *          and doing no echo at all.
  220.  */
  221. ttgetc()
  222. {
  223.     register int c;
  224.  
  225.     if((c = fgetc(stdin)) == EOF){
  226.     if(errno == EINTR)        /* only one that might be OK */
  227.       return(NODATA);
  228.     kill(getpid(), SIGHUP);        /* the only honorable thing to do */
  229.     }
  230.     else
  231.       return(c & 0xff);            /* 8 bit clean! */
  232. }
  233.  
  234.  
  235. #if    TYPEAH
  236. /* 
  237.  * typahead - Check to see if any characters are already in the
  238.  *          keyboard buffer
  239.  */
  240. typahead()
  241. {
  242.     int x;    /* holds # of pending chars */
  243.  
  244.     return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
  245. }
  246. #endif
  247.  
  248.  
  249. /*
  250.  * GetKey - Read in a key.
  251.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  252.  * character set.  Resolves escape sequences and returns no-op if global
  253.  * timeout value exceeded.
  254.  */
  255. GetKey()
  256. {
  257.     int    c;
  258.  
  259.     if(timeout){
  260.     if(setjmp(alrm_state) == 0){
  261.         alarm((unsigned)timeout);
  262.         errno = 0;
  263.         c = (*term.t_getchar)();
  264.         if(errno == EINTR)
  265.           c = NODATA;
  266.     }
  267.     else
  268.       c = NODATA;                /* alarm went off so go back */
  269.  
  270.     alarm(0);                /* shut off the alarm */
  271.     if(c == NODATA)
  272.       return(c);
  273.     }
  274.     else{
  275.     c = (*term.t_getchar)();
  276.     }
  277.  
  278.     if (c == METACH) {                      /* Apply M- prefix      */
  279.     int status;
  280.         
  281.     /*
  282.      * this code should intercept special keypad keys
  283.      */
  284.  
  285.     switch(status = kbseq(&c)){
  286.       case 0 :     /* no dice */
  287.         return(c);
  288.       case  K_PAD_UP        :
  289.       case  K_PAD_DOWN        :
  290.       case  K_PAD_RIGHT        :
  291.       case  K_PAD_LEFT        :
  292.       case  K_PAD_PREVPAGE    :
  293.       case  K_PAD_NEXTPAGE    :
  294.       case  K_PAD_HOME        :
  295.         return(status);
  296.       case F1  :
  297.       case F2  :
  298.       case F3  :
  299.       case F4  :
  300.       case F5  :
  301.       case F6  :
  302.       case F7  :
  303.       case F8  :
  304.       case F9  :
  305.       case F10 :
  306.       case F11 :
  307.       case F12 :
  308.         return(status);
  309.       case BADESC :
  310.       default :                /* punt the whole thing    */
  311.         (*term.t_beep)();
  312.         break;
  313.     }
  314.     }
  315.  
  316.     if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  317.       c = CTRL | (c+'@');
  318.  
  319.     return (c);
  320.  
  321. }
  322.  
  323.  
  324.  
  325. /* 
  326.  * kbseq - looks at an escape sequence coming from the keyboard and 
  327.  *         compares it to a trie of known keyboard escape sequences, and
  328.  *         performs the function bound to the escape sequence.
  329.  * 
  330.  *         returns: BADESC, the escaped function, or 0 if not found.
  331.  */
  332. kbseq(c)
  333. int    *c;
  334. {
  335.     register char    b;
  336.     register int    first = 1;
  337.     register struct    KBSTREE    *current = kpadseqs;
  338.  
  339.     if(kpadseqs == NULL)            /* bag it */
  340.       return(BADESC);
  341.  
  342.     while(1){
  343.     *c = b = (*term.t_getchar)();
  344.  
  345.     while(current->value != b){
  346.         if(current->left == NULL){        /* NO MATCH */
  347.         if(first)
  348.           return(BADESC);
  349.         else
  350.           return(0);
  351.         }
  352.         current = current->left;
  353.     }
  354.  
  355.     if(current->down == NULL)        /* match!!!*/
  356.       return(current->func);
  357.     else
  358.       current = current->down;
  359.  
  360.     first = 0;
  361.     }
  362. }
  363.  
  364.  
  365.  
  366. /*
  367.  * alt_editor - fork off an alternate editor for mail message composition 
  368.  *              if one is configured and passed from pine.  If not, only
  369.  *              ask for the editor if advanced user flag is set, and 
  370.  *              suggest environment's EDITOR value as default.
  371.  */
  372. alt_editor(f, n)
  373. {
  374.     char   eb[NLINE];                /* buf holding edit command */
  375.     char   *fn;                    /* tmp holder for file name */
  376.     char   *cp;
  377.     char   *args[MAXARGS];            /* ptrs into edit command */
  378.     char   *writetmp();
  379.     int       child, pid, i, done = 0;
  380.     long   l;
  381. #ifdef POSIX
  382.     int    stat;
  383. #else
  384.     union  wait stat;
  385. #endif
  386.     FILE   *p;
  387.     SIGTYPE (*ohup)(), (*oint)(), (*osize)(), (*ostop)(), (*ostart)();
  388.  
  389.     if(Pmaster == NULL)
  390.       return;
  391.  
  392.     if(gmode&MDSCUR){
  393.     emlwrite("Alternate editor not available in restricted mode", NULL);
  394.     return;
  395.     }
  396.  
  397.     if(Pmaster->alt_ed == NULL){
  398.     if(!(gmode&MDADVN)){
  399.         emlwrite("\007Unknown Command",NULL);
  400.         return;
  401.     }
  402.  
  403.     if(getenv("EDITOR"))
  404.       strcpy(eb, (char *)getenv("EDITOR"));
  405.     else
  406.       *eb = '\0';
  407.  
  408.     while(!done){
  409.         pid = mlreplyd("Which alternate editor ? ", eb, NLINE, QDEFLT);
  410.  
  411.         switch(pid){
  412.           case ABORT:
  413.         return(-1);
  414.           case HELPCH:
  415.         emlwrite("no alternate editor help yet");
  416.  
  417. /* take sleep and break out after there's help */
  418.         sleep(3);
  419.         break;
  420.           case (CTRL|'L'):
  421.         sgarbf = TRUE;
  422.         update();
  423.         break;
  424.           case TRUE:
  425.           case FALSE:            /* does editor exist ? */
  426.         if(*eb == '\0'){        /* leave silently? */
  427.             mlerase();
  428.             return(-1);
  429.         }
  430.  
  431.         done++;
  432.         break;
  433.           default:
  434.         break;
  435.         }
  436.     }
  437.     }
  438.     else
  439.       strcpy(eb, Pmaster->alt_ed);
  440.  
  441.     if((fn=writetmp(0, 1)) == NULL){        /* get temp file */
  442.     emlwrite("Problem writing temp file for alt editor");
  443.     return(-1);
  444.     }
  445.  
  446.     strcat(eb, " ");
  447.     strcat(eb, fn);
  448.  
  449.     cp = eb;
  450.     for(i=0; *cp != '\0';i++){            /* build args array */
  451.     if(i < MAXARGS){
  452.         args[i] = NULL;            /* in case we break out */
  453.     }
  454.     else{
  455.         emlwrite("Too many args for command!");
  456.         return(-1);
  457.     }
  458.  
  459.     while(isspace(*cp))
  460.       if(*cp != '\0')
  461.         cp++;
  462.       else
  463.         break;
  464.  
  465.     args[i] = cp;
  466.  
  467.     while(!isspace(*cp))
  468.       if(*cp != '\0')
  469.         cp++;
  470.       else
  471.         break;
  472.  
  473.     if(*cp != '\0')
  474.       *cp++ = '\0';
  475.     }
  476.  
  477.     args[i] = NULL;
  478.  
  479.     if(Pmaster)
  480.       (*Pmaster->raw_io)(0);            /* turn OFF raw mode */
  481.  
  482.     if(child=fork()){            /* wait for the child to finish */
  483.     ohup = signal(SIGHUP, SIG_IGN);    /* ignore signals for now */
  484.     oint = signal(SIGINT, SIG_IGN);
  485. #ifdef    TIOCGWINSZ
  486.         osize = signal(SIGWINCH, SIG_IGN);
  487. #endif
  488.  
  489. /*
  490.  * bug - wait should be made non-blocking and mail_pings or something 
  491.  * need to be done in the loop to keep the imap stream alive
  492.  */
  493.     while((pid=wait(&stat)) != child)
  494.       ;
  495.  
  496.     signal(SIGHUP, ohup);    /* restore signals */
  497.     signal(SIGINT, oint);
  498. #ifdef    TIOCGWINSZ
  499.         signal(SIGWINCH, osize);
  500. #endif
  501.     }
  502.     else{                /* spawn editor */
  503.     signal(SIGHUP, SIG_DFL);    /* let editor handle signals */
  504.     signal(SIGINT, SIG_DFL);
  505.     signal(SIGALRM, SIG_DFL);
  506. #ifdef    TIOCGWINSZ
  507.         signal(SIGWINCH, SIG_DFL);
  508. #endif
  509.     if(execvp(args[0], args) < 0)
  510.       exit(1);
  511.     }
  512.  
  513.     if(Pmaster)
  514.       (*Pmaster->raw_io)(1);        /* turn ON raw mode */
  515.  
  516.     /*
  517.      * replace edited text with new text 
  518.      */
  519.     curbp->b_flag &= ~BFCHG;        /* make sure old text gets blasted */
  520.     readin(fn, 0);            /* read new text overwriting old */
  521.     unlink(fn);                /* blast temp file */
  522.     curbp->b_flag |= BFCHG;        /* mark dirty for packbuf() */
  523.     ttopen();                /* reset the signals */
  524.     refresh(0, 1);            /* redraw */
  525. }
  526.  
  527.  
  528.  
  529. /*
  530.  *  bktoshell - suspend and wait to be woken up
  531.  */
  532. bktoshell()        /* suspend MicroEMACS and wait to wake up */
  533. {
  534. #ifdef    SIGTSTP
  535.     int pid;
  536.     int rtfrmshell();
  537.     extern int vttidy();
  538.  
  539.     if(!(gmode&MDSSPD)){
  540.     emlwrite("\007Unknown command: ^Z");
  541.     return;
  542.     }
  543.  
  544.     if(Pmaster){
  545.     (*Pmaster->raw_io)(0);    /* actually in pine source */
  546.  
  547.     movecursor(term.t_nrow, 0);
  548.     printf("\n\n\nUse \"fg\" to return to Pine\n");
  549.  
  550.     }
  551.     else
  552.       vttidy();
  553.  
  554.     movecursor(term.t_nrow, 0);
  555.     peeol();
  556.     (*term.t_flush)();
  557.  
  558.     signal(SIGCONT, (SIGTYPE *)rtfrmshell);    /* prepare to restart */
  559.     signal(SIGTSTP, SIG_DFL);            /* prepare to stop */
  560.     pid = getpid();
  561.     kill(pid, SIGTSTP);
  562. #endif
  563. }
  564.  
  565.  
  566. /* 
  567.  * rtfrmshell - back from shell, fix modes and return
  568.  */
  569. rtfrmshell()
  570. {
  571. #ifdef    SIGCONT
  572.     signal(SIGCONT, SIG_DFL);
  573.  
  574.     if(Pmaster){
  575.     if(Pmaster)
  576.       (*Pmaster->raw_io)(1);        /* actually in pine source */
  577.     (*Pmaster->keybinit)(gmode&MDFKEY);    /* using f-keys? */
  578.     }
  579.  
  580.     ttopen();
  581.  
  582.     sgarbf = TRUE;
  583.     curwp->w_flag = WFHARD;
  584.     refresh();
  585. #endif
  586. }
  587.  
  588.  
  589.  
  590. /*
  591.  * do_alarm_signal - jump back in the stack to where we can handle this
  592.  */
  593. SIGTYPE do_alarm_signal()
  594. {
  595.     signal(SIGALRM, (SIGTYPE *)do_alarm_signal);
  596.     longjmp(alrm_state, 1);
  597. }
  598.  
  599.  
  600.  
  601. /*
  602.  * do_hup_signal - jump back in the stack to where we can handle this
  603.  */
  604. SIGTYPE do_hup_signal()
  605. {
  606.     if(Pmaster){
  607.     extern  jmp_buf   finstate;
  608.  
  609.     signal(SIGHUP, (SIGTYPE *)do_hup_signal);
  610.     longjmp(finstate, COMP_GOTHUP);
  611.     }
  612.     else{
  613.     /*
  614.      * if we've been interrupted and the buffer is changed,
  615.      * save it...
  616.      */
  617.     if(anycb() == TRUE){            /* time to save */
  618.         if(curbp->b_fname[0] == '\0'){    /* name it */
  619.         strcpy(curbp->b_fname, "pico.save");
  620.         }
  621.         else{
  622.         strcat(curbp->b_fname, ".save");
  623.         }
  624.         writeout(curbp->b_fname);
  625.     }
  626.     vttidy();
  627.     exit(1);
  628.     }
  629. }
  630.  
  631.  
  632. /*
  633.  * big bitmap of ASCII characters allowed in a file name
  634.  * (needs reworking for other char sets)
  635.  */
  636. char okinfname[32] = {
  637.       0,    0,             /* ^@ - ^G, ^H - ^O  */
  638.       0,    0,            /* ^P - ^W, ^X - ^_  */
  639.       0,    0x17,        /* SP - ' ,  ( - /   */
  640.       0xff, 0xc0,        /*  0 - 7 ,  8 - ?   */
  641.       0x7f, 0xff,        /*  @ - G ,  H - O   */
  642.       0xff, 0xe1,        /*  P - W ,  X - _   */
  643.       0x7f, 0xff,        /*  ` - g ,  h - o   */
  644.       0xff, 0xe2,        /*  p - w ,  x - DEL */
  645.       0,    0,             /*  > DEL   */
  646.       0,    0,            /*  > DEL   */
  647.       0,    0,             /*  > DEL   */
  648.       0,    0,             /*  > DEL   */
  649.       0,    0             /*  > DEL   */
  650. };
  651.  
  652.  
  653. /*
  654.  * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
  655.  */
  656. fallowc(c)
  657. char c;
  658. {
  659.     return(okinfname[c>>3] & 0x80>>(c&7));
  660. }
  661.  
  662.  
  663. /*
  664.  * fexist - returns TRUE if the file exists with mode passed in m, 
  665.  *          FALSE otherwise.  By side effect returns length of file in l
  666.  */
  667. fexist(file, m, l)
  668. char *file;
  669. char *m;                    /* files mode: r, w or rw */
  670. long *l;
  671. {
  672.     struct stat    sbuf;
  673.  
  674.     if(l)
  675.       *l = 0L;
  676.  
  677.     if(stat(file, &sbuf) < 0){
  678.     switch(errno){
  679.       case ENOENT :                /* File not found */
  680.         return(FIOFNF);
  681.         break;
  682.       case ENAMETOOLONG :            /* Name is too long */
  683.         return(FIOLNG);
  684.         break;
  685.       default:                /* Some other error */
  686.         return(FIOERR);
  687.         break;
  688.     }
  689.     }
  690.  
  691.     if(l)
  692.       *l = sbuf.st_size;
  693.  
  694.     if((sbuf.st_mode&S_IFMT) == S_IFDIR)
  695.       return(FIODIR);
  696.  
  697.     if(m[0] == 'r')                /* read access? */
  698.       return((S_IREAD&sbuf.st_mode) ? FIOSUC : FIONRD);
  699.     else if(m[0] == 'w')            /* write access? */
  700.       return((S_IWRITE&sbuf.st_mode) ? FIOSUC : FIONWT);
  701.     else if(m[0] == 'x')            /* execute access? */
  702.       return((S_IEXEC&sbuf.st_mode) ? FIOSUC : FIONEX);
  703.     return(FIOERR);                /* what? */
  704. }
  705.  
  706.  
  707. /*
  708.  * isdir - returns true if fn is a readable directory, false otherwise
  709.  *         silent on errors (we'll let someone else notice the problem;)).
  710.  */
  711. isdir(fn, l)
  712. char *fn;
  713. long *l;
  714. {
  715.     struct stat sbuf;
  716.  
  717.     if(l)
  718.       *l = 0;
  719.  
  720.     if(stat(fn, &sbuf) < 0)
  721.       return(0);
  722.  
  723.     if(l)
  724.       *l = sbuf.st_size;
  725.     return((sbuf.st_mode&S_IFMT) == S_IFDIR);
  726. }
  727.  
  728.  
  729. #if    defined(bsd) || defined(nxt) || defined(dyn)
  730. /*
  731.  * getcwd - NeXT uses getwd()
  732.  */
  733. char *getcwd(pth)
  734. char *pth;
  735. {
  736.     extern char *getwd();
  737.  
  738.     return(getwd(pth));
  739. }
  740. #endif
  741.  
  742.  
  743. /*
  744.  * gethomedir - returns the users home directory
  745.  *              Note: home is malloc'd for life of pico
  746.  */
  747. char *gethomedir(l)
  748. int *l;
  749. {
  750.     static char *home = NULL;
  751.     static short hlen = 0;
  752.  
  753.     if(home == NULL){
  754.     strcpy(s, "~");
  755.     fixpath(s, NLINE);        /* let fixpath do the work! */
  756.     hlen = strlen(s);
  757.     if((home=(char *)malloc((strlen(s) + 1) * sizeof(char))) == NULL){
  758.         emlwrite("Problem allocating space for home dir", NULL);
  759.         return(0);
  760.     }
  761.     strcpy(home, s);
  762.     }
  763.  
  764.     if(l)
  765.       *l = hlen;
  766.  
  767.     return(home);
  768. }
  769.  
  770.  
  771. /*
  772.  * homeless - returns true if given file does not reside in the current
  773.  *            user's home directory tree. 
  774.  */
  775. homeless(f)
  776. char *f;
  777. {
  778.     char *home;
  779.     int   len;
  780.  
  781.     home = gethomedir(&len);
  782.     return(strncmp(home, f, len));
  783. }
  784.  
  785.  
  786.  
  787. /*
  788.  * errstr - return system error string corresponding to given errno
  789.  *          Note: strerror() is not provided on all systems, so it's 
  790.  *          done here once and for all.
  791.  */
  792. char *errstr(err)
  793. int err;
  794. {
  795.     extern char *sys_errlist[];
  796.     extern int  sys_nerr;
  797.  
  798.     return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
  799. }
  800.  
  801.  
  802.  
  803. /*
  804.  * getfnames - return all file names in the given directory in a single 
  805.  *             malloc'd string.  n contains the number of names
  806.  */
  807. char *getfnames(dn, n)
  808. char *dn;
  809. int  *n;
  810. {
  811.     int status;
  812.     long l;
  813.     char *names, *np, *p;
  814.     struct stat sbuf;
  815.     DIR *dirp;                        /* opened directory */
  816. #if    defined(POSIX) || defined(aix)
  817.     struct dirent *dp;
  818. #else
  819.     struct direct *dp;
  820. #endif
  821.  
  822.     *n = 0;
  823.  
  824.     if(stat(dn, &sbuf) < 0){
  825.     switch(errno){
  826.       case ENOENT :                /* File not found */
  827.         emlwrite("\007File not found: \"%s\"", dn);
  828.         break;
  829.       case ENAMETOOLONG :            /* Name is too long */
  830.         emlwrite("\007File name too long: \"%s\"", dn);
  831.         break;
  832.       default:                /* Some other error */
  833.         emlwrite("\007Error getting file info: \"%s\"", dn);
  834.         break;
  835.     }
  836.     return(NULL);
  837.     } 
  838.     else{
  839.     l = sbuf.st_size;
  840.     if((sbuf.st_mode&S_IFMT) != S_IFDIR){
  841.         emlwrite("\007Not a directory: \"%s\"", dn);
  842.         return(NULL);
  843.     }
  844.     }
  845.  
  846.     if((names=(char *)malloc(sizeof(char)*l)) == NULL){
  847.     emlwrite("\007Can't malloc space for file names");
  848.     return(NULL);
  849.     }
  850.  
  851.     errno = 0;
  852.     if((dirp=opendir(dn)) == NULL){
  853.     sprintf(s,"\007Can't open \"%s\": %s", dn, errstr(errno));
  854.     emlwrite(s);
  855.     free((char *)names);
  856.     return(NULL);
  857.     }
  858.  
  859.     np = names;
  860.     while((dp = readdir(dirp)) != NULL){
  861.     (*n)++;
  862.     p = dp->d_name;
  863.     while((*np++ = *p++) != '\0')
  864.       ;
  865.     }
  866.  
  867.     closedir(dirp);                    /* shut down */
  868.     return(names);
  869. }
  870.  
  871.  
  872. /*
  873.  * fioperr - given the error number and file name, display error
  874.  */
  875. fioperr(e, f)
  876. int  e;
  877. char *f;
  878. {
  879.     switch(e){
  880.       case FIOFNF:                /* File not found */
  881.     emlwrite("\007File \"%s\" not found", f);
  882.     break;
  883.       case FIOEOF:                /* end of file */
  884.     emlwrite("\007End of file \"%s\" reached", f);
  885.     break;
  886.       case FIOLNG:                /* name too long */
  887.     emlwrite("\007File name \"%s\" too long", f);
  888.     break;
  889.       case FIODIR:                /* file is a directory */
  890.     emlwrite("\007File \"%s\" is a directory", f);
  891.     break;
  892.       case FIONWT:
  893.     emlwrite("\007Write permission denied: %s", f);
  894.     break;
  895.       case FIONRD:
  896.     emlwrite("\007Read permission denied: %s", f);
  897.     break;
  898.       case FIONEX:
  899.     emlwrite("\007Execute permission denied: %s", f);
  900.     break;
  901.       default:
  902.     emlwrite("\007File I/O error: %s", f);
  903.     }
  904. }
  905.  
  906.  
  907.  
  908. /*
  909.  * pfnexpand - pico's function to expand the given file name if there is 
  910.  *           a leading '~'
  911.  */
  912. char *pfnexpand(fn, len)
  913. char *fn;
  914. int  len;
  915. {
  916.     struct passwd *pw;
  917.     struct passwd *getpwnam(), *getpwuid();
  918.     register char *x, *y, *z;
  919.     char name[20];
  920.     
  921.     if(*fn == '~') {
  922.         for(x = fn+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++);
  923.         *y = '\0';
  924.         if(x == fn + 1) 
  925.           pw = getpwuid(getuid());
  926.         else
  927.           pw = getpwnam(name);
  928.         if(pw == NULL)
  929.           return(NULL);
  930.         if(strlen(pw->pw_dir) + strlen(fn) > len) {
  931.             return(NULL);
  932.         }
  933.     /* make room for expanded path */
  934.     for(z=x+strlen(x),y=fn+strlen(x)+strlen(pw->pw_dir);
  935.         z >= x;
  936.         *y-- = *z--);
  937.     /* and insert the expanded address */
  938.     for(x=fn,y=pw->pw_dir; *y != '\0'; *x++ = *y++);
  939.     }
  940.     return(fn);
  941. }
  942.  
  943.  
  944.  
  945. /*
  946.  * fixpath - make the given pathname into an absolute path
  947.  */
  948. fixpath(name, len)
  949. char *name;
  950. int  len;
  951. {
  952.     register char *shft;
  953.  
  954.     if(*name != '/'){                /* filenames relative to ~ */
  955.     if(Pmaster && (*name != '~' && strlen(name)+2 <= len)){
  956.  
  957.         for(shft = strchr(name, '\0'); shft >= name; shft--)
  958.           shft[2] = *shft;
  959.  
  960.         name[0] = '~';
  961.         name[1] = '/';
  962.     }
  963.  
  964.     pfnexpand(name, len);
  965.     }
  966. }
  967.  
  968.  
  969. /*
  970.  * compresspath - given a base path and an additional directory, collapse
  971.  *                ".." and "." elements and return absolute path (appending
  972.  *                base if necessary).  
  973.  *
  974.  *                returns  1 if OK, 
  975.  *                         0 if there's a problem
  976.  *                         new path, by side effect, if things went OK
  977.  */
  978. compresspath(base, path, len)
  979. char *base, *path;
  980. int  len;
  981. {
  982.     register int i;
  983.     int  depth = 0;
  984.     char *p;
  985.     char *stack[32];
  986.  
  987. #define PUSHD(X)  (stack[depth++] = X)
  988. #define POPD()    ((depth > 0) ? stack[--depth] : "")
  989.  
  990.     if(*path == '~'){
  991.     fixpath(path, len);
  992.     strcpy(s, path);
  993.     }
  994.     else if(*path != C_FILESEP)
  995.       sprintf(s, "%s%c%s", base, C_FILESEP, path);
  996.     else
  997.       strcpy(s, path);
  998.  
  999.     p = s;
  1000.     for(i=0; s[i] != '\0'; i++){        /* pass thru path name */
  1001.     if(s[i] == '/'){
  1002.         if(p != s)
  1003.           PUSHD(p);                /* push dir entry */
  1004.         p = &s[i+1];            /* advance p */
  1005.         s[i] = '\0';            /* cap old p off */
  1006.         continue;
  1007.     }
  1008.  
  1009.     if(s[i] == '.'){            /* special cases! */
  1010.         if(s[i+1] == '.'            /* parent */
  1011.            && (s[i+2] == '/' || s[i+2] == '\0')){
  1012.         if(!strcmp(POPD(),""))        /* bad news! */
  1013.           return(0);
  1014.  
  1015.         i += 2;
  1016.         p = (s[i] == '\0') ? "" : &s[i+1];
  1017.         }
  1018.         else if(s[i+1] == '/' || s[i+1] == '\0'){        /* no op */
  1019.         i++;
  1020.         p = (s[i] == '\0') ? "" : &s[i+1];
  1021.         }
  1022.     }
  1023.     }
  1024.  
  1025.     if(*p != '\0')
  1026.       PUSHD(p);                    /* get last element */
  1027.  
  1028.     path[0] = '\0';
  1029.     for(i = 0; i < depth; i++){
  1030.     strcat(path, S_FILESEP);
  1031.     strcat(path, stack[i]);
  1032.     }
  1033.  
  1034.     return(1);                    /* everything's ok */
  1035. }
  1036.  
  1037.  
  1038. /*
  1039.  * tmpname - return a temporary file name in the given buffer
  1040.  */
  1041. tmpname(name)
  1042. char *name;
  1043. {
  1044.     sprintf(name, "/tmp/pico.%d", getpid());    /* tmp file name */
  1045. }
  1046.  
  1047.  
  1048. /*
  1049.  * Take a file name, and from it
  1050.  * fabricate a buffer name. This routine knows
  1051.  * about the syntax of file names on the target system.
  1052.  * I suppose that this information could be put in
  1053.  * a better place than a line of code.
  1054.  */
  1055. makename(bname, fname)
  1056. char    bname[];
  1057. char    fname[];
  1058. {
  1059.         register char   *cp1;
  1060.         register char   *cp2;
  1061.  
  1062.         cp1 = &fname[0];
  1063.         while (*cp1 != 0)
  1064.                 ++cp1;
  1065.  
  1066.         while (cp1!=&fname[0] && cp1[-1]!='/')
  1067.                 --cp1;
  1068.         cp2 = &bname[0];
  1069.         while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  1070.                 *cp2++ = *cp1++;
  1071.         *cp2 = 0;
  1072. }
  1073.  
  1074.  
  1075. /*
  1076.  * copy - copy contents of file 'a' into a file named 'b'.  Return error
  1077.  *        if either isn't accessible or is a directory
  1078.  */
  1079. copy(a, b)
  1080. char *a, *b;
  1081. {
  1082.     int    in, out, n, rv = 0;
  1083.     char   *cb;
  1084.     struct stat tsb, fsb;
  1085.     extern int  errno;
  1086.  
  1087.     if(stat(a, &fsb) < 0){        /* get source file info */
  1088.     emlwrite("Can't Copy: %s", errstr(errno));
  1089.     return(-1);
  1090.     }
  1091.  
  1092.     if(!(fsb.st_mode&S_IREAD)){        /* can we read it? */
  1093.     emlwrite("\007Read permission denied: %s", a);
  1094.     return(-1);
  1095.     }
  1096.  
  1097.     if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
  1098.     emlwrite("\007Can't copy: %s is a directory", a);
  1099.     return(-1);
  1100.     }
  1101.  
  1102.     if(stat(b, &tsb) < 0){        /* get dest file's mode */
  1103.     switch(errno){
  1104.       case ENOENT:
  1105.         break;            /* these are OK */
  1106.       default:
  1107.         emlwrite("\007Can't Copy: %s", errstr(errno));
  1108.         return(-1);
  1109.     }
  1110.     }
  1111.     else{
  1112.     if(!(tsb.st_mode&S_IWRITE)){    /* can we write it? */
  1113.         emlwrite("\007Write permission denied: %s", b);
  1114.         return(-1);
  1115.     }
  1116.  
  1117.     if((tsb.st_mode&S_IFMT) == S_IFDIR){    /* is it directory? */
  1118.         emlwrite("\007Can't copy: %s is a directory", b);
  1119.         return(-1);
  1120.     }
  1121.  
  1122.     if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
  1123.         emlwrite("\007Identical files.  File not copied", NULL);
  1124.         return(-1);
  1125.     }
  1126.     }
  1127.  
  1128.     if((in = open(a, O_RDONLY)) < 0){
  1129.     emlwrite("Copy Failed: %s", errstr(errno));
  1130.     return(-1);
  1131.     }
  1132.  
  1133.     if((out=creat(b, fsb.st_mode&0xfff)) < 0){
  1134.     emlwrite("Can't Copy: %s", errstr(errno));
  1135.     close(in);
  1136.     return(-1);
  1137.     }
  1138.  
  1139.     if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
  1140.     emlwrite("Can't allocate space for copy buffer!", NULL);
  1141.     close(in);
  1142.     close(out);
  1143.     return(-1);
  1144.     }
  1145.  
  1146.     while(1){                /* do the copy */
  1147.     if((n = read(in, cb, NLINE)) < 0){
  1148.         emlwrite("Can't Read Copy: %s", errstr(errno));
  1149.         rv = -1;
  1150.         break;            /* get out now */
  1151.     }
  1152.  
  1153.     if(n == 0)            /* done! */
  1154.       break;
  1155.  
  1156.     if(write(out, cb, n) != n){
  1157.         emlwrite("Can't Write Copy: %s", errstr(errno));
  1158.         rv = -1;
  1159.         break;
  1160.     }
  1161.     }
  1162.  
  1163.     free(cb);
  1164.     close(in);
  1165.     close(out);
  1166.     return(rv);
  1167. }
  1168.  
  1169.  
  1170. /*
  1171.  * Open a file for writing. Return TRUE if all is well, and FALSE on error
  1172.  * (cannot create).
  1173.  */
  1174. ffwopen(fn)
  1175. char    *fn;
  1176. {
  1177.         extern FILE *ffp;
  1178.  
  1179.         if ((ffp=fopen(fn, "w")) == NULL) {
  1180.                 emlwrite("Cannot open file for writing");
  1181.                 return (FIOERR);
  1182.         }
  1183.         return (FIOSUC);
  1184. }
  1185.  
  1186.  
  1187. /*
  1188.  * Close a file. Should look at the status in all systems.
  1189.  */
  1190. ffclose()
  1191. {
  1192.         extern FILE *ffp;
  1193.  
  1194.         if (fclose(ffp) != FALSE) {
  1195.                 emlwrite("Error closing file");
  1196.                 return(FIOERR);
  1197.         }
  1198.         return(FIOSUC);
  1199. }
  1200.  
  1201.  
  1202. /*
  1203.  * P_open - run the given command in a sub-shell returning a file pointer
  1204.  *        from which to read the output
  1205.  *
  1206.  * note:
  1207.  *    For OS's other than unix, you will have to rewrite this function.
  1208.  *    Hopefully it'll be easy to exec the command into a temporary file, 
  1209.  *    and return a file pointer to that opened file or something.
  1210.  */
  1211. FILE *P_open(s)
  1212. char *s;
  1213. {
  1214.     return(popen(s, "r"));
  1215. }
  1216.  
  1217.  
  1218.  
  1219. /*
  1220.  * P_close - close the given descriptor
  1221.  *
  1222.  */
  1223. P_close(fp)
  1224. FILE *fp;
  1225. {
  1226.     return(pclose(fp));
  1227. }
  1228.  
  1229.  
  1230.  
  1231. /*
  1232.  * worthit - generic sort of test to roughly gage usefulness of using 
  1233.  *           optimized scrolling.
  1234.  *
  1235.  * note:
  1236.  *    returns the line on the screen, l, that the dot is currently on
  1237.  */
  1238. worthit(l)
  1239. int *l;
  1240. {
  1241.     int i;            /* l is current line */
  1242.     unsigned below;        /* below is avg # of ch/line under . */
  1243.  
  1244.     *l = doton(&i, &below);
  1245.     below = (i > 0) ? below/(unsigned)i : 0;
  1246.  
  1247.     return(below > 3);
  1248. }
  1249.  
  1250.  
  1251.  
  1252. /*
  1253.  * pico_new_mail - just checks mtime and atime of mail file and notifies user 
  1254.  *               if it's possible that they have new mail.
  1255.  */
  1256. pico_new_mail()
  1257. {
  1258.     int ret = 0;
  1259.     static time_t lastchk = 0;
  1260.     struct stat sbuf;
  1261.     char   inbox[256];
  1262.  
  1263.     sprintf(inbox,"%s/%s", MAILDIR, getlogin());
  1264.     if(stat(inbox, &sbuf) == 0){
  1265.     ret = sbuf.st_atime <= sbuf.st_mtime &&
  1266.       (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
  1267.     lastchk = sbuf.st_mtime;
  1268.     return(ret);
  1269.     }
  1270.     else
  1271.       return(ret);
  1272. }
  1273.  
  1274.  
  1275.  
  1276. /*
  1277.  * time_to_check - checks the current time against the last time called 
  1278.  *                 and returns true if the elapsed time is > timeout
  1279.  */
  1280. time_to_check()
  1281. {
  1282.     static time_t lasttime = 0L;
  1283.  
  1284.     if(!timeout)
  1285.       return(FALSE);
  1286.  
  1287.     if(time((long *) 0) - lasttime > (time_t)timeout){
  1288.     lasttime = time((long *) 0);
  1289.     return(TRUE);
  1290.     }
  1291.     else
  1292.       return(FALSE);
  1293. }
  1294.  
  1295.  
  1296. /*
  1297.  * sstrcasecmp - compare two pointers to strings case independently
  1298.  */
  1299. sstrcasecmp(s1, s2)
  1300. char **s1, **s2;
  1301. {
  1302.     register char *a, *b;
  1303.  
  1304.     a = *s1;
  1305.     b = *s2;
  1306.     while(toupper(*a) == toupper(*b++))
  1307.     if(*a++ == '\0')
  1308.       return(0);
  1309.  
  1310.     return(toupper(*a) - toupper(*--b));
  1311. }
  1312.  
  1313.  
  1314. #ifdef    TIOCGWINSZ
  1315. /*
  1316.  * winch_handler - handle window change signal
  1317.  */
  1318. SIGTYPE winch_handler()
  1319. {
  1320.     struct winsize win;
  1321.     extern int resize_pico();
  1322.  
  1323.     signal(SIGWINCH, (SIGTYPE *)winch_handler);
  1324.  
  1325.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {
  1326.     if (win.ws_col && win.ws_row)
  1327.       resize_pico(win.ws_row - 1, win.ws_col);
  1328.     }
  1329. }
  1330. #endif    /* TIOCGWINSZ */
  1331.